/* Copyright (c) 2024 Qualcomm Technologies International, Ltd. */
/* %%version */

#include "gatt_ots_client_common_util.h"
#include "gatt_ots_client_filter_object.h"
#include "csr_time.h"

#define OTSC_MAX_FILTER_COUNT                          (3)

#define FILTER_VALUE_SIZE                              (1) /* one octet */

/* Spec defined different filter values */
typedef CsrUint8                                       OtscFilterValues;
#define  OTSC_NO_FILTER                                (OtscFilterValues)(0x00)
/* Name filters */
#define  OTSC_FILTER_BY_NAME_STARTS_WITH               (OtscFilterValues)(0x01)
#define  OTSC_FILTER_BY_NAME_ENDS_WITH                 (OtscFilterValues)(0x02)
#define  OTSC_FILTER_BY_NAME_CONTAINS                  (OtscFilterValues)(0x03)
#define  OTSC_FILTER_BY_NAME_EXACT_MATCH               (OtscFilterValues)(0x04)
/*Object UUID filter */
#define  OTSC_FILTER_BY_OBJECT_UUID_TYPE               (OtscFilterValues)(0x05)
/* Date filters */
#define  OTSC_FILTER_BY_DATE_RANGE_CREATED             (OtscFilterValues)(0x06)
#define  OTSC_FILTER_BY_DATE_RANGE_MODIFIED            (OtscFilterValues)(0x07)
/* Object size filters */
#define  OTSC_FILTER_BY_CURRENT_SIZE_RANGE             (OtscFilterValues)(0x08)
#define  OTSC_FILTER_BY_ALLOCATED_SIZE_RANGE           (OtscFilterValues)(0x09)
/* Marked object filter */
#define  OTSC_FILTER_BY_MARKED_OBJECTS                 (OtscFilterValues)(0x0A)

static void otscSendFilterObjectCfm(GOTSC *const    gattOtsClient,
                                    CsrBtResultCode result,
                                    CsrBtSupplier   supplier)
{
    GattOtscFilterObjectsCfm *message = CsrPmemZalloc(sizeof(*message));

    message->svcHandle  = gattOtsClient->srvcElem->service_handle;
    message->resultCode = result;
    message->supplier   = supplier;

    /* Reset the pending cmd flag*/
    gattOtsClient->pendingCmd &= ~OTSC_PENDING_OP_FILTER_OBJ;
    gattOtsClient->filtersCount = 0;

    GATT_OTS_CLIENT_INFO("GOTSC: otscSendFilterObjectCfm: svcHandle[%0x]\n", gattOtsClient->srvcElem->service_handle);

    /* Send the confirmation message to app task  */
    GattOtscMessageSend(gattOtsClient->appTask, GATT_OTSC_FILTER_OBJECTS_CFM, message);
}

/* This function finds first three filters and discard other filters(if any) */
static void otscFindFirstThreeFilters(GOTSC *const gattOtsClient,
                                      GattOtscObjectFilters filterBitmask,
                                      GattOtscObjectFilters *filtersArray)
{
    uint8 filterCount = OTSC_MAX_FILTER_COUNT;
    uint16 filter     = GATT_OTSC_FILTER_BY_NAME_STARTS_WITH;
    uint8 i = 0;

    while (filterCount && filter <= GATT_OTSC_FILTER_BY_MARKED_OBJECTS)
    {
        if ((filterBitmask & filter) != GATT_OTSC_FILTER_NONE)
        {
            filtersArray[i++] = filterBitmask & filter;
            gattOtsClient->filtersCount++; /* track the no of filter count */
            filterCount--;
        }
        filter = filter << 1;
    }
}

static void otscFreeFilterParams(OtscInternalMsgFilterObject* msg)
{
    if (msg->nameInfo != NULL)
    {
        if (msg->nameInfo->startName != NULL)
        {
            CsrPmemFree(msg->nameInfo->startName);
            msg->nameInfo->startName = NULL;
        }
        if (msg->nameInfo->endName != NULL)
        {
            CsrPmemFree(msg->nameInfo->endName);
            msg->nameInfo->endName = NULL;
        }
        if (msg->nameInfo->nameContains != NULL)
        {
            CsrPmemFree(msg->nameInfo->nameContains);
            msg->nameInfo->nameContains = NULL;
        }
        if (msg->nameInfo->exactName != NULL)
        {
            CsrPmemFree(msg->nameInfo->exactName);
            msg->nameInfo->exactName = NULL;
        }
        CsrPmemFree(msg->nameInfo);
        msg->nameInfo = NULL;
    }

    if (msg->dateInfo != NULL)
    {
        CsrPmemFree(msg->dateInfo);
        msg->dateInfo = NULL;
    }

    if (msg->objSizeInfo != NULL)
    {
        CsrPmemFree(msg->objSizeInfo);
        msg->objSizeInfo = NULL;
    }
}

static CsrBool otscValidateFilterParams(OtscInternalMsgFilterObject* msg)
{
    GattOtscObjectFilters filter;
    CsrBool validated = TRUE;

    for (filter = GATT_OTSC_FILTER_BY_NAME_STARTS_WITH;
         filter < GATT_OTSC_FILTER_BY_MARKED_OBJECTS; /* shifting it down */ )
    {
        switch(msg->filters & filter)
        {
            case GATT_OTSC_FILTER_BY_NAME_STARTS_WITH:
            {
                if (msg->nameInfo == NULL ||
                    msg->nameInfo->startName == NULL)
                {
                    validated = FALSE;
                }
                break;
            }
            case GATT_OTSC_FILTER_BY_NAME_ENDS_WITH:
            {
                if (msg->nameInfo == NULL ||
                    msg->nameInfo->endName == NULL)
                {
                    validated = FALSE;
                }
                break;
            }
            case GATT_OTSC_FILTER_BY_NAME_CONTAINS:
            {
                if (msg->nameInfo == NULL ||
                    msg->nameInfo->nameContains == NULL)
                {
                    validated = FALSE;
                }
                break;
            }
            case GATT_OTSC_FILTER_BY_NAME_EXACT_MATCH:
            {
                if (msg->nameInfo == NULL ||
                    msg->nameInfo->exactName == NULL)
                {
                    validated = FALSE;
                }
                break;
            }
            case GATT_OTSC_FILTER_BY_OBJECT_UUID_TYPE:
            {
                if (msg->objectUuid.length == CSR_BT_UUID16_SIZE)
                {
                    uint8 zeroUuid16[CSR_BT_UUID16_SIZE] = {0};
                    if (CsrMemCmp(&msg->objectUuid.uuid, &zeroUuid16, CSR_BT_UUID16_SIZE) == 0)
                    {
                        validated = FALSE;
                    }
                }
                else if (msg->objectUuid.length == CSR_BT_UUID128_SIZE)
                {
                    uint8 zeroUuid128[CSR_BT_UUID128_SIZE] = {0};
                    if (CsrMemCmp(&msg->objectUuid.uuid, &zeroUuid128, CSR_BT_UUID128_SIZE) == 0)
                    {
                        validated = FALSE;
                    }
                }
                else
                {
                    validated = FALSE;
                }
                break;
            }
            case GATT_OTSC_FILTER_BY_DATE_RANGE_CREATED:
            case GATT_OTSC_FILTER_BY_DATE_RANGE_MODIFIED:
            { /* Not supported at present */
                validated = FALSE;
                break;
            }
            case GATT_OTSC_FILTER_BY_CURRENT_SIZE_RANGE:
            {
                if (msg->objSizeInfo == NULL                ||
                    msg->objSizeInfo->currentSizeStart == 0 ||
                    msg->objSizeInfo->currentSizeEnd   == 0)
                {
                    validated = FALSE;
                }
                break;
            }
            case GATT_OTSC_FILTER_BY_ALLOCATED_SIZE_RANGE:
            {
                if (msg->objSizeInfo == NULL                  ||
                    msg->objSizeInfo->allocatedSizeStart == 0 ||
                    msg->objSizeInfo->allocatedSizeEnd   == 0)
                {
                    validated = FALSE;
                }
                break;
            }
            default:
            {
                break;
            }
        }

        if (validated == FALSE)
        {
            break;
        }
        filter = filter << 1;
    }

    return (validated);
}

static ServiceHandle otscFindUnusedFilterHandle(GOTSC *const gattOtsClient, uint8 filterCount)
{
    if (filterCount == 0)
        return (gattOtsClient->deviceData.objListFilterHandle1);
    else if (filterCount == 1)
        return (gattOtsClient->deviceData.objListFilterHandle2);
    else
        return (gattOtsClient->deviceData.objListFilterHandle3);
}

bool GattOtscFilterObjectsReq(ServiceHandle                svcHandle,
                              GattOtscObjectFilters        filters,         /* bitmask of max three filters to use */
                              GattOtscFilterByNameInfo*    nameInfo,
                              GattOtscFilterByDateInfo*    dateInfo,
                              GattOtscFilterByObjSizeInfo* objSizeInfo,
                              CsrBtUuid                    objectUuid)
{
    GOTSC *gattOtsClient = ServiceHandleGetInstanceData(svcHandle);

    GATT_OTS_CLIENT_INFO("GOTSC: GattOtscFilterObjectsReq : svcHandle[%0x4x] filters[%0x]\n", svcHandle, filters);

    if (gattOtsClient)
    {
        if ((gattOtsClient->pendingCmd & OTSC_PENDING_OP_FILTER_OBJ)     ||
            (gattOtsClient->pendingCmd & OTSC_PENDING_OP_OLCP_PROCEDURE) ||
            (gattOtsClient->pendingCmd & OTSC_PENDING_OP_OACP_PROCEDURE))
        {
            GATT_OTS_CLIENT_ERROR("GOTSC: Filter objects or OACP/OLCP procedure in progress!\n");
            return (FALSE);
        }
        else if ((gattOtsClient->deviceData.objListControlPointHandle) &&
                 (gattOtsClient->deviceData.objListFilterHandle1 ||
                  gattOtsClient->deviceData.objListFilterHandle2 ||
                  gattOtsClient->deviceData.objListFilterHandle3))
        {
            OtscInternalMsgFilterObject *message = CsrPmemZalloc(sizeof(*message));

            message->svcHandle     = svcHandle;
            message->filters       = filters;

            if (nameInfo)
            {
                message->nameInfo = CsrPmemZalloc(sizeof(GattOtscFilterByNameInfo));

                if (nameInfo->startName)
                {
                    message->nameInfo->startName = CsrPmemZalloc(CsrStrLen((const CsrCharString *)nameInfo->startName) + 1);
                    CsrMemCpy(message->nameInfo->startName, nameInfo->startName, CsrStrLen((const CsrCharString *)nameInfo->startName));
                    CsrPmemFree(nameInfo->startName);
                }
                if (nameInfo->endName)
                {
                    message->nameInfo->endName = CsrPmemZalloc(CsrStrLen((const CsrCharString *)nameInfo->endName) + 1);
                    CsrMemCpy(message->nameInfo->endName, nameInfo->endName, CsrStrLen((const CsrCharString *)nameInfo->endName));
                    CsrPmemFree(nameInfo->endName);
                }
                if (nameInfo->nameContains)
                {
                    message->nameInfo->nameContains = CsrPmemZalloc(CsrStrLen((const CsrCharString *)nameInfo->nameContains) + 1);
                    CsrMemCpy(message->nameInfo->nameContains, nameInfo->nameContains, CsrStrLen((const CsrCharString *)nameInfo->nameContains));
                    CsrPmemFree(nameInfo->nameContains);
                }
                if (nameInfo->exactName)
                {
                    message->nameInfo->exactName = CsrPmemZalloc(CsrStrLen((const CsrCharString *)nameInfo->exactName) + 1);
                    CsrMemCpy(message->nameInfo->exactName, nameInfo->exactName, CsrStrLen((const CsrCharString *)nameInfo->exactName));
                    CsrPmemFree(nameInfo->exactName);
                }

                CsrPmemFree(nameInfo);
            }
            if (dateInfo)
            {
                message->dateInfo = CsrPmemZalloc(sizeof(GattOtscFilterByDateInfo));
                CsrMemCpy(message->dateInfo, dateInfo, sizeof(GattOtscFilterByDateInfo));
                CsrPmemFree(dateInfo);
            }
            if (objSizeInfo)
            {
                message->objSizeInfo = CsrPmemZalloc(sizeof(GattOtscFilterByObjSizeInfo));
                CsrMemCpy(message->objSizeInfo, objSizeInfo, sizeof(GattOtscFilterByObjSizeInfo));
                CsrPmemFree(objSizeInfo);
            }

            CsrMemCpy(&message->objectUuid, &objectUuid, sizeof(CsrBtUuid));

            gattOtsClient->pendingCmd |= OTSC_PENDING_OP_FILTER_OBJ;

            GattOtscMessageSend(gattOtsClient->libTask,
                                OTSC_INTERNAL_MSG_FILTER_OBJ_REQ,
                                message);
            return (TRUE);
        }
        else
        {
            GATT_OTS_CLIENT_ERROR("GOTSC: OLCP/Filter characteristic not supported!\n");
            return (FALSE);
        }
    }
    else
    {
        GATT_OTS_CLIENT_ERROR("GOTSC: Invalid OTS Client instance!\n");
        return (FALSE);
    }
}

void OtscHandleInternalMsgFilterObject(GOTSC *const gattOtsClient, OtscInternalMsgFilterObject* msg)
{
    CsrUint8 fCount       = OTSC_MAX_FILTER_COUNT;
    CsrUint8 *value       = NULL;
    CsrUint8 valueSize   = 0;
    GattOtscObjectFilters filtersArray[OTSC_MAX_FILTER_COUNT] = {GATT_OTSC_FILTER_NONE};

    /* Reset the filter counter, if any from previous call */
    gattOtsClient->filtersCount = 0;

    /* Validate the filter params if not-NONE filter is passed */
    if (msg->filters != GATT_OTSC_FILTER_NONE)
    {
        if (otscValidateFilterParams(msg) == FALSE)
        {
            GATT_OTS_CLIENT_ERROR("GOTSC: Filter validation failed !\n");
            otscSendFilterObjectCfm(gattOtsClient,
                                    GATT_OTSC_RESULT_CODE_INVALID_PARAMETER,
                                    GATT_SUPPLIER_OTS_CLIENT);
        }
        else
        {
            /* Get first three filters(at max) from app's requested filters bitmask */
            otscFindFirstThreeFilters(gattOtsClient, msg->filters, filtersArray);
        }
    }
    else
    {
        /* Simply set the filter count to max such that no filter is applied
         * to all of the filter characteristic instances */
        gattOtsClient->filtersCount = OTSC_MAX_FILTER_COUNT;
    }

    /* Run the loop to set filter for all three instances of filter characteristics at server */
    for (fCount = 0; fCount < gattOtsClient->filtersCount; fCount++)
    {
        valueSize = 0;

        if (msg->filters == GATT_OTSC_FILTER_NONE)
        {
            valueSize = FILTER_VALUE_SIZE;
            value     = (CsrUint8 *)CsrPmemAlloc(valueSize);
            *value    = OTSC_NO_FILTER;

            CsrBtGattWriteReqSend(gattOtsClient->srvcElem->gattId,
                                  gattOtsClient->srvcElem->cid,
                                  otscFindUnusedFilterHandle(gattOtsClient, fCount),
                                  0,
                                  valueSize,
                                  value);
        }
        else
        {
            if (filtersArray[fCount] != GATT_OTSC_FILTER_NONE)
            {
                if (filtersArray[fCount] == GATT_OTSC_FILTER_BY_NAME_STARTS_WITH)
                {
                    valueSize = (CsrUint8)(CsrStrLen((const CsrCharString*)msg->nameInfo->startName) + 1);
                    value     = (CsrUint8 *)CsrPmemZalloc(valueSize + 1);

                    value[0]  = OTSC_FILTER_BY_NAME_STARTS_WITH;
                    CsrMemCpy(&value[1], (CsrUint8 *)msg->nameInfo->startName, valueSize);
                }
                else if (filtersArray[fCount] == GATT_OTSC_FILTER_BY_NAME_ENDS_WITH)
                {
                    valueSize = (CsrUint8)(CsrStrLen((const CsrCharString*)msg->nameInfo->endName) + 1);
                    value     = (CsrUint8 *)CsrPmemZalloc(valueSize + 1);

                    value[0]  = OTSC_FILTER_BY_NAME_ENDS_WITH;
                    CsrMemCpy(&value[1], (CsrUint8 *)msg->nameInfo->endName, valueSize);
                }
                else if (filtersArray[fCount] == GATT_OTSC_FILTER_BY_NAME_CONTAINS)
                {
                    valueSize = (CsrUint8)(CsrStrLen((const CsrCharString*)msg->nameInfo->nameContains) + 1);
                    value     = (CsrUint8 *)CsrPmemZalloc(valueSize + 1);

                    value[0]  = OTSC_FILTER_BY_NAME_CONTAINS;
                    CsrMemCpy(&value[1], (CsrUint8 *)msg->nameInfo->nameContains, valueSize);
                }
                else if (filtersArray[fCount] == GATT_OTSC_FILTER_BY_NAME_EXACT_MATCH)
                {
                    valueSize = (CsrUint8)(CsrStrLen((const CsrCharString*)msg->nameInfo->exactName) + 1);
                    value     = (CsrUint8 *)CsrPmemZalloc(valueSize + 1);

                    value[0]  = OTSC_FILTER_BY_NAME_EXACT_MATCH;
                    CsrMemCpy(&value[1], (CsrUint8 *)msg->nameInfo->exactName, valueSize);
                }
                else if (filtersArray[fCount] == GATT_OTSC_FILTER_BY_OBJECT_UUID_TYPE)
                {
                    valueSize = (CsrUint8)(msg->objectUuid.length) + 1;
                    value     = (CsrUint8 *)CsrPmemZalloc(valueSize + 1);

                    value[0]  = OTSC_FILTER_BY_OBJECT_UUID_TYPE;
                    CsrMemCpy(&value[1], (CsrUint8 *)&msg->objectUuid.uuid, valueSize);
                }
                else if (filtersArray[fCount] == GATT_OTSC_FILTER_BY_CURRENT_SIZE_RANGE)
                {
                    valueSize = 2 *  OTSC_OBJECT_SIZE + 1;
                    value     = (CsrUint8 *) CsrPmemAlloc(valueSize);

                    value[0]  = OTSC_FILTER_BY_CURRENT_SIZE_RANGE;
                    CsrMemCpy(&value[1], (CsrUint8 *)&msg->objSizeInfo->currentSizeStart, OTSC_OBJECT_SIZE);
                    CsrMemCpy(&value[5], (CsrUint8 *)&msg->objSizeInfo->currentSizeEnd, OTSC_OBJECT_SIZE);
                }
                else if (filtersArray[fCount] == GATT_OTSC_FILTER_BY_ALLOCATED_SIZE_RANGE)
                {
                    valueSize = 2 *  OTSC_OBJECT_SIZE + 1;
                    value     = (CsrUint8 *) CsrPmemAlloc(valueSize);

                    value[0]  = OTSC_FILTER_BY_ALLOCATED_SIZE_RANGE;
                    CsrMemCpy(&value[1], (CsrUint8 *)&msg->objSizeInfo->allocatedSizeStart, OTSC_OBJECT_SIZE);
                    CsrMemCpy(&value[5], (CsrUint8 *)&msg->objSizeInfo->allocatedSizeEnd, OTSC_OBJECT_SIZE);
                }
                else
                { /* GATT_OTSC_FILTER_BY_MARKED_OBJECTS */
                    valueSize = FILTER_VALUE_SIZE;
                    value     = (CsrUint8 *) CsrPmemAlloc(valueSize);
                    value[0]  = OTSC_FILTER_BY_MARKED_OBJECTS;
                }

                if (valueSize != 0)
                {
                    CsrBtGattWriteReqSend(gattOtsClient->srvcElem->gattId,
                                          gattOtsClient->srvcElem->cid,
                                          otscFindUnusedFilterHandle(gattOtsClient, fCount),
                                          0,
                                          valueSize,
                                          value);
                }
            }
        }
    }

    /* Free all filter params passed by application */
    otscFreeFilterParams(msg);
}

void OtscHandleWriteCfmToFilterObject(GOTSC *const gattOtsClient,
                                      const CsrBtGattWriteCfm *cfm)
{
    if (cfm->handle == gattOtsClient->deviceData.objListFilterHandle1 ||
        cfm->handle == gattOtsClient->deviceData.objListFilterHandle2 ||
        cfm->handle == gattOtsClient->deviceData.objListFilterHandle3)
    {
        gattOtsClient->filtersCount--;

        if (gattOtsClient->filtersCount == 0)
        {/* All write CFM received */
            otscSendFilterObjectCfm(gattOtsClient,
                                    GATT_OTSC_RESULT_CODE_SUCCESS,
                                    GATT_SUPPLIER_OTS_CLIENT);
        }
    }
}

